/**
 * 
 */
package org.swing.utility.sytem.jar;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.ProviderNotFoundException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

/**
 * @author lqnhu
 *
 */
public class PathClassLoader extends FlexibleClassLoader {
	private final Object[] paths;

	public PathClassLoader(Path[] paths, ClassLoader parent, boolean childFirst)
			throws IOException {
		super(parent, childFirst);
		this.paths = process(paths);
	}

	public PathClassLoader(Path[] paths, boolean childFirst) throws IOException {
		super(childFirst);
		this.paths = process(paths);
	}

	public PathClassLoader(Path[] paths, ClassLoader parent) throws IOException {
		this(paths, parent, false);
	}

	public PathClassLoader(Path[] paths) throws IOException {
		this(paths, false);
	}

	private static Object[] process(Path[] paths) throws IOException {
		try {
			final Object[] os = new Object[paths.length];
			for (int i = 0; i < paths.length; i++) {
				final Path p = paths[i];
				final Object o;
				if (Files.isRegularFile(p))
					o = FileSystems.newFileSystem(p, null);
				else
					o = p;
				os[i] = o;
			}
			return os;
		} catch (ProviderNotFoundException e) {
			throw new RuntimeException(e);
		}
	}

	public final void close() {
		IOException ex = null;
		for (Object p : paths) {
			if (p instanceof FileSystem) {
				try {
					((FileSystem) p).close();
				} catch (IOException e) {
					if (ex == null)
						ex = e;
					else
						ex.addSuppressed(e);
				}
			}
		}
	}

	private Path resolve(Object o, String name) {
		final Path p;
		if (o instanceof FileSystem)
			p = ((FileSystem) o).getPath(name);
		else
			p = ((Path) o).resolve(name);
		return p;
	}

	@Override
	protected URL findResource1(String name) {
		try {
			for (Object o : paths) {
				final Path p = resolve(o, name);
				if (Files.exists(p))
					return p.toUri().toURL();
			}
			return null;
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	protected Enumeration<URL> findResources1(String name) {
		try {
			final List<URL> urls = new ArrayList<URL>();
			for (Object o : paths) {
				final Path p = resolve(o, name);
				if (Files.exists(p))
					urls.add(p.toUri().toURL());
			}
			return Collections.enumeration(urls);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	protected InputStream findResourceAsStream(String name) {
		try {
			for (Object o : paths) {
				final Path p = resolve(o, name);
				if (isFileResource(p))
					return Files.newInputStream(p);
			}
			return null;
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	@Override
	protected byte[] readResource(String name) {
		try {
			for (Object o : paths) {
				final Path p = resolve(o, name);
				if (isFileResource(p))
					return Files.readAllBytes(p);
			}
			return null;
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
	}

	private boolean isFileResource(Path p) {
		return Files.exists(p) && !Files.isDirectory(p);
	}
}
